import main from "./index";
import { Newe, Gete, ColorToRGB } from "./utils";
import { IData } from "./models";

interface IMarker {
  left: number, // 标注处的位置(旗杆脚下)
  top: number,
  width: number,
  height: number,
  name: string,
  dataUrl: string,
  sprite: { left: number, top: number }, // 主画布里精灵的位置
  output: { left: number, top: number }, // 输出画布里精灵的位置
  bmpData: any[],
  rgbIndexData: number[],
}

export default class {
  Data: IMarker[] = [];
  private id = 0; // 自增
  private flagLayer: HTMLDivElement;
  
  constructor() {
    this.flagLayer = Gete("flagLayer") as HTMLDivElement;
  }
  
  private flags() {
    return document.getElementsByClassName("flag");
  }
  
  SetScale(scale: number) {
    const flags = this.flags();
    for (let i = 0; i < flags.length; i += 1) {
      const flag = flags[i] as HTMLDivElement;
      flag.style.left = flag["originalLeft"] * scale - 5 + "px";
      flag.style.top = flag["originalTop"] * scale - 32 + "px";
    }
  }
  
  private newMarker(mx: number, my: number) {
    const { left, top, width, height } = main.Canvas.GetRect(mx, my);
    if (width <= 0 || height <= 0) return null;
    
    const { bmpData, rgbIndexData } = this.sampling(left, top, width, height);
    for (let i = 0; i < bmpData.length; i += 1)
      for (let j = 0; j < bmpData[i].length; j += 1)
        bmpData[i][j] = bmpData[i][j].toString().replaceAll(",", "");
    
    const canvas = Newe({
      type: "canvas", para: { width, height },
    }) as HTMLCanvasElement;
    const ct = canvas.getContext("2d");
    ct.drawImage(
      main.Canvas.Dom,
      left, top, width, height,
      0, 0, width, height,
    );
    
    // 获得一个可用名称，不可重名！
    let name = "";
    while (true) {
      name = "obj" + this.id.toString();
      if (!this.IsDupName(name)) break;
      this.id++;
    }
    
    const markerItem: IMarker = {
      left: mx, top: my,
      width, height,
      name,
      dataUrl: canvas.toDataURL("image/png"),
      sprite: { left, top },
      output: { left: 0, top: 0 },
      bmpData,
      rgbIndexData,
    };
    return markerItem;
  }
  
  private create(marker: IMarker) {
    this.id++;
    this.Data.push(marker);
    const markerIndex = this.Data.length - 1;
    this.addFlag(markerIndex, marker.left, marker.top);
    main.Canvas.SetRect(markerIndex);
    main.Canvas.UpdateCut();
    main.ObjList.Add(markerIndex);
    main.Output.DrawImage(markerIndex);
  }
  
  private addFlag(markerIndex: number, mx: number, my: number) {
    const { Scale } = main.Toolbar;
    const flag = Newe({ para: { className: "flag" } });
    flag.style.left = mx * Scale - 5 + "px"; // 位置是旗杆脚下，而不是左上角。
    flag.style.top = my * Scale - 32 + "px";
    flag["originalLeft"] = mx; // 自定义属性，这谁拦得住！
    flag["originalTop"] = my;
    const flagIndex = Newe({
      textContent: markerIndex.toString(),
      para: { onclick: (e: MouseEvent) => {
        e.stopPropagation(); // 阻止父容器接收到点击事件
        const index = parseInt(e.target["textContent"]);
        this.Remove(index);
      }},
    });
    flag.append(flagIndex);
    this.flagLayer.append(flag);
  }
  
  Remove(markerIndex: number) {
    main.Canvas.SetRect(markerIndex, false); // 恢复脚下占用的土地
    
    const { Data } = this;
    Data.splice(markerIndex, 1);
    
    // 重建索引
    const flags = this.flags();
    flags[markerIndex].remove();
    for (let i = markerIndex; i < Data.length; i += 1) {
      const flag = flags[i];
      if (flag) flag.firstChild.textContent = i.toString();
    }
    
    main.ObjList.Del(markerIndex);
    main.Output.Update();
    main.Feedback.Update();
    if (!Data.length) {
      this.id = 0;
      main.Canvas.UpdateCut();
      main.Toolbar.SetBtnEnable(false);
    }
  }
  
  Clear() {
    while (1) {
      const flags = this.flags();
      if (!flags.length) break;
      flags[0].remove();
    }
    
    this.Data.map((_, i) => main.Canvas.SetRect(i, false));
    this.Data = [];
    this.id = 0;
    
    main.Canvas.UpdateCut();
    main.Toolbar.SetBtnEnable(false);
  }
  
  Click(mx: number, my: number) {
    for (let i = 0; i < this.Data.length; i += 1) {
      const markerItem = this.Data[i];
      const { left, top } = markerItem;
      if (left === mx && top === my) return; // 防止手抖造成的重复点击
    }
    
    const marker = this.newMarker(mx, my);
    if (!marker) return;
    
    this.create(marker);
    
    main.Feedback.Update();
    main.Toolbar.SetBtnEnable(true);
  }
  
  // 生成精灵图的颜色分层数据，一种颜色对应一组点阵。
  private sampling(left: number, top: number, width: number, height: number) {
    const bottom = top + height, right = left + width;
    const layerData = [];
    for (let i = top; i < bottom; i += 1) {
      layerData.push([]);
      for (let j = left; j < right; j += 1)
        layerData[i - top].push("0");
    }
    
    const { colorData, palette } = main.Canvas.Data[main.FileList.FileName] as IData;
    const bmpData = [];
    const rgbIndexData: number[] = [];
    const rgbData: string[] = [];
    for (let i = top; i < bottom; i += 1) {
      for (let j = left; j < right; j += 1) {
        const color = colorData[i][j];
        if (main.Canvas.IsTransparent(color, 0)) continue; // 略过透明色
        
        const rgb = ColorToRGB(color);
        const rgbIndex = rgbData.indexOf(rgb);
        if (rgbIndex === -1) {
          rgbData.push(rgb);
          rgbIndexData.push(palette.indexOf(rgb));
          const layer: [] = JSON.parse(JSON.stringify(layerData));
          bmpData.push(layer);
        }
        const bmpIndex = rgbIndex >= 0 ? rgbIndex : bmpData.length - 1;
        bmpData[bmpIndex][i - top][j - left] = "1";
      }
    }
    
    return { bmpData, rgbIndexData };
  }
  
  // 重新采样，当更换原图时，更新每个标注点的"dataUrl"。
  Refresh() {
    const canvas = Newe({ type: "canvas" }) as HTMLCanvasElement;
    const ct = canvas.getContext("2d");
    const markerData: IMarker[] = [];
    for (let i = 0; i < this.Data.length; i += 1) {
      const markerItem = this.Data[i];
      const { width, height, sprite } = markerItem;
      const { left, top } = sprite;
      canvas.width = width;
      canvas.height = height;
      ct.drawImage(
        main.Canvas.Dom,
        left, top, width, height,
        0, 0, width, height,
      );
      markerItem.dataUrl = canvas.toDataURL("image/png");
      markerData.push(markerItem);
    }
    this.Data = markerData;
  }
  
  IsDupName(newName: string, ignoreIndex = -1) {
    let isRepeat = false;
    for (let i = 0; i < this.Data.length; i += 1) {
      const markerItem = this.Data[i];
      if (i === ignoreIndex) continue;
      
      if (markerItem.name === newName) {
        isRepeat = true;
        break;
      }
    }
    return isRepeat;
  }
  
  AutoPick() {
    const [l, t, w, h] = main.Canvas.CutRect;
    const r = l + w, b = t + h;
    const canvas = Newe({ type: "canvas" }) as HTMLCanvasElement;
    const ct = canvas.getContext("2d");
    
    for (let y = t; y < b; y += 1)
      for (let x = l; x < r; x += 1) {
        const { left, top, width, height } = main.Canvas.GetRect(x, y);
        if (width < 8 || height < 8) continue;
        
        const { bmpData, rgbIndexData } = this.sampling(left, top, width, height);
        for (let i = 0; i < bmpData.length; i += 1)
          for (let j = 0; j < bmpData[i].length; j += 1)
            bmpData[i][j] = bmpData[i][j].toString().replaceAll(",", "");
        
        canvas.width = width;
        canvas.height = height;
        ct.drawImage(
          main.Canvas.Dom,
          left, top, width, height,
          0, 0, width, height,
        );
        
        const marker: IMarker = {
          left: left + Math.round(width / 2),
          top: top + Math.round(height / 2),
          width, height,
          name: "obj" + this.id.toString(),
          dataUrl: canvas.toDataURL("image/png"),
          sprite: { left, top },
          output: { left: 0, top: 0 },
          bmpData,
          rgbIndexData,
        };
        this.create(marker);
      }
    
    main.Feedback.Update();
    main.Toolbar.SetBtnEnable(this.Data.length > 0);
  }
};
